#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/times.h>
#include	<string.h>
#include	<locale.h>

#undef equal
#define equal(x,y)	!strcmp(x,y)
#define SECSINDAY (60*60*24)

/* get clock ticks per second */
#include <time.h>

#ifndef CLK_TCK
#if defined (_SC_CLK_TCK)
#define CLK_TCK sysconf(_SC_CLK_TCK)
#else
#include <sys/param.h>
#ifdef HZ
#define CLK_TCK HZ
#else
#if defined(ultrix)
#define CLK_TCK 60
#else
error here
#endif
#endif
#endif
#endif

#ifdef __cplusplus
extern "C"
#endif

#define MAXNAMES 5000	/*  Maximum number of transactions types */
#define NHRS	24

static	int maxtypes = MAXNAMES;
static	unsigned numtypes;		/*  current # distinct xact types */
static	int hourtotal[NHRS];	/*  Hourly total number ... all transactions */
static	int hourtime[NHRS];	/*  Hourly total time ... all transactions */

static	struct XACT {
	char name[20];			/* Transaction name */
	int totcount;			/* total # invocations */
	int tottime;			/* total amt of time */
	int hrcount[NHRS];		/* # occurrances in each hour */
	int hrtime[NHRS];		/* total time for each hour */
} trans[MAXNAMES];



static	int flg_sa,flg_eb;
static	time_t tstrt_a,tend_b;
static	char strt_a[50];
static	char end_b[50];
static	int sortbytime;

static  long    convtime (char *);
static  long    tmsecs (register struct tm *, register struct tm *);
static	void	setnames (char *);
static  void    convday (register char *);
static  void    usage (void);
static  int     righttime (long, long);
static  int     tmless (register struct tm *, register struct tm *);
static  struct XACT *lookup (char *);

main(argc,argv)
int argc;
char *argv[];
{
	long pid;
	long duration; 
	long tbegin, tend;
	long tbeginL, tendL;
	long clk_tck;	/* some systems call the OS to get it - cache it*/
	char line[200],xactnam[40];
	struct tm strt;
	struct XACT *t;
	int i,j;
	int daytime=0, daytotal=0;
	float avg;
	char temp[50];
	char timestr[100];


	/* initialize the locale */
	(void) setlocale(LC_ALL, "");

	(void) time(&tend_b);
	tstrt_a = tend_b - SECSINDAY;

	setbuf(stdout, NULL);
	while((i = getopt(argc, argv,"tn:d:s:e:")) != EOF) {
		switch(i){
			case 't':
				sortbytime++;
				break;

			case 'n':
				setnames(optarg);
				break;

			case 'd':
				convday(optarg);
				tstrt_a = convtime("00:");
				tend_b = convtime("24:");
				break;

			case 's':
				flg_sa++;
				(void) strcpy(strt_a, optarg);
				break;

			case 'e':
				flg_eb++;
				(void) strcpy(end_b, optarg);
				break;

			default:
				usage();
				exit(1);
		}
	}
	clk_tck = CLK_TCK;
	if(flg_sa)	tstrt_a = convtime(strt_a);
	if(flg_eb)	tend_b = convtime(end_b);

	if (tstrt_a > tend_b){
		(void) fprintf(stderr,"inconsistent time specs\n");
	}
	/* print selected times in s, S, e, E once for each file; */
	(void) strftime(timestr, sizeof(timestr), "%c\n",
		localtime(&tstrt_a));
	if (flg_sa) (void) printf("START AFTER:\t%s", timestr);

	(void) strftime(timestr, sizeof(timestr), "%c\n",
		localtime(&tend_b));
	if (flg_eb) (void) printf("END BEFORE:\t%s", timestr);

	while (fgets(line, sizeof(line), stdin) != NULL)
	{
		if(line[0] != '@') continue;
		if(sscanf(&line[1],"%s %ld %ld %ld %ld %ld",
				xactnam,&pid,&tbegin,&tbeginL,&tend,&tendL) != 6)
			continue;

		if(!righttime(tbegin,tend)) continue;

		t = lookup(xactnam);
		if(t == NULL) continue;

		duration = tendL - tbeginL;
		strt = *(localtime((time_t *)&tbegin));

		t->hrcount[strt.tm_hour]++;
		t->hrtime[strt.tm_hour] += (int) duration;

			/* save for column (hourly) summary */
		hourtotal[strt.tm_hour]++;
		hourtime[strt.tm_hour] += (int) duration;

			/* save for row summary */
		t->totcount++;
		t->tottime += (int) duration;
	}

	(void)printf("      SERVICE SUMMARY REPORT\n\n\n");

	(void)printf("%-16s","SVCNAME");
	for(i=0;i<NHRS;i++){
/* 		if(hourtotal[i]){ */
			if((i>0) && (i<11)){
				(void) sprintf(temp,"%da-%da",i,i+1);
				(void) printf("%-12s",temp);
				continue;
			}
			if((i>12) && (i< 23)){
				(void) sprintf(temp,"%dp-%dp",i,i+1);
				(void) printf("%-12s",temp);
				continue;
			}
			if(i==0){
				(void) printf("%-12s","12m-1a");
				continue;
			}
			if(i == 11){
				(void) printf("%-12s","11a-12n");
				continue;
			}
			if(i == 12){
				(void) printf("%-12s","12n-1p");
				continue;
			}
			if(i == 23){
				(void) printf("%-12s","11p-12m");
				continue;
			}
/* 		} */
	}
	(void) printf("\tTOTALS\n");
	(void) printf("%-16s","");
	for(i=0;i<NHRS;i++){
/* 		if(hourtotal[i])
			(void) printf("%-12s","Num/Avg");
	}
	(void) printf("\tNum/Avg\n");

	(void) printf("%-16s","---------------");
	for(i=0;i<NHRS;i++){
/*		if(hourtotal[i]) */
			(void) printf("%-12s","--------");
	}
	(void) printf("\t-------\n");


	for (i=0,t=trans; i<numtypes; i++,t++){
		(void) printf("%-16.15s", trans[i].name);

		for(j=0;j<NHRS;j++){
/*			if(hourtotal[j]){ */
				/* print row/column entry */
				if(!t->hrcount[j]) 
				{
				   avg = 0;
/*				   t->hrcount[j] = 0; */
				}	
				else{
				   avg = ((float)t->hrtime[j]/clk_tck)/t->hrcount[j];
				}
				(void) sprintf(temp,"%d/%.2f",t->hrcount[j],avg);
				(void) printf("%-12s",temp);
/*			}*/
		}

			/* print row summary */
		if(!t->totcount) 
		{
		   avg = 0;
		   t->totcount = 0;
		}	
                else{
			avg = ((float)t->tottime/clk_tck)/t->totcount;
		}
		(void) printf("\t%d/%.2f", t->totcount, avg);
		(void) printf("\n");
	}


	(void) printf("%-16s","---------------");
	for(i=0;i<NHRS;i++){
/*		if(hourtotal[i]) */
			(void) printf("%-12s","-------");
	}
	(void) printf("\t-------\n");
	(void) printf("%-16s","TOTALS");

		/* print column (hourly) summaries */
	for(i=0;i<NHRS;i++){
/*		if(!hourtotal[i]) continue; */
		if(!hourtotal[i]) 
		{
			avg = 0;
		}
		else{
			daytotal += hourtotal[i];
			daytime += hourtime[i];

			avg = ((float) hourtime[i]/clk_tck) / hourtotal[i];
		}
                (void) sprintf(temp,"%d/%.2f", hourtotal[i], avg);
		(void) printf("%-12s",temp);
	}


		/* print final summary */
	if(daytotal == 0) avg = 0;
	else avg = ((float)daytime/clk_tck) / daytotal;

	(void) printf("\t%d/%.2f\n", daytotal, avg);
	exit(0);
	/* NOTREACHED */
	return(0);
}

static	int
righttime(tbegin,tend)
long tbegin, tend;
{
	if((tbegin > tstrt_a) && (tend <= tend_b)){
			/* xact executed within interval */
		return(1);
	}
	return(0);
}

static	struct XACT *
lookup(name)
char *name;
{
	int i;

	for (i=0; i<numtypes && !(equal(name,trans[i].name)); i++);

	if (i == numtypes){
		if (numtypes == maxtypes){
			return(NULL);
		}

                        /*   Make new entry in table    */

		(void) strcpy(trans[numtypes].name, name);
		numtypes++;
	}
	return(&trans[i]);
}

static	void
setnames(p)
char *p;
{
	char *endp,*trailp,*leadp;

	endp = p + strlen(p);
	for(trailp=leadp=p; trailp <= endp;) {
		while((*leadp != 0) && (*leadp != ','))
			leadp++;
		*leadp = 0;
		if(lookup(trailp) == NULL){
			(void) fprintf(stderr,"error populating name table");
			(void) fprintf(stderr," with -n arguments");
			exit(1);
		}
		leadp++;
		trailp = leadp;
	}
	maxtypes = numtypes;
	return;
}


static	int
numcompar(x,y)
struct XACT *x, *y;
{
	/* sort high to low */
	return(((struct XACT *)y)->totcount - ((struct XACT *)x)->totcount);
}

static	int
timcompar(x,y)
struct XACT *x, *y;
{
	/* sort high to low */
	return(((struct XACT *)y)->tottime - ((struct XACT *)x)->tottime);
}

static	void
usage()
{
	(void) fprintf(stderr,"txrpt usage:\n");
	(void) fprintf(stderr,"txprt [-t] [-n names] [-d mm/dd] ");
	(void) fprintf(stderr,"[-s time] [-e time] ");
	(void) fprintf(stderr,"\n");
}

/*
	the following routines were swiped from the acctcom command
*/

static	time_t	daydiff;

/*
 * convday computes number of seconds to be subtracted
 * because of -d mon/day argument
 */
static	void
convday(str)
register char *str;
{
	struct tm *cur;
	time_t tcur;
	int mday, mon;
	register int i;

	if (sscanf(str, "%d/%d", &mon, &mday) != 2) {
		(void) fprintf(stderr, "txrpt: bad -d %s\n", str);
		exit(1);
	}
	mon--;
	(void) time(&tcur);
	daydiff = tcur;
	for (i = 0; i < 100; i++) {
		cur = localtime(&tcur);
		if (mday == cur->tm_mday && mon == cur->tm_mon) {
			daydiff -= tcur;
			return;
		}
		tcur -= SECSINDAY;
	}
	daydiff = 0;
	(void) fprintf(stderr, "txrpt: bad -d %s\n", str);
	exit(1);
}

/*
 * convtime converts time arg to internal value
 * arg has form hr:min:sec, min or sec are assumed to be 0 if omitted
 * times assumed to be within last 24 hours, unless -d mo/dy given before
 */
static	long
convtime(str)
char *str;
{
	struct tm *cur, arg;
	time_t tcur;

	arg.tm_min = 0;
	arg.tm_sec = 0;
	if (sscanf(str, "%d:%d:%d", &arg.tm_hour, &arg.tm_min,
		&arg.tm_sec) < 1) {
		usage();
		exit(1);
	}
	(void) time(&tcur);
	cur = localtime(&tcur);
	if (tmless(&arg, cur))
		tcur -= (daydiff+tmsecs(&arg, cur));
	else {
		tcur += tmsecs(cur, &arg);
		if (daydiff)
			tcur -= daydiff;
	}
	return(tcur);
}

/*
 *	return 1 if t1 earlier than t2 (times in localtime format)
 *	assumed that t1 and t2 are in same day
 */

static	int
tmless(t1, t2)
register struct tm *t1, *t2;
{
	if (t1->tm_hour != t2->tm_hour)
		return(t1->tm_hour < t2->tm_hour);
	if (t1->tm_min != t2->tm_min)
		return(t1->tm_min < t2->tm_min);
	return(t1->tm_sec < t2->tm_sec);
}

/*
 *	tmsecs returns number of seconds from t1 to t2,
 *	times expressed in localtime format.
 *	assumed that t1 <= t2, and are in same day.
 */

static	long
tmsecs(t1, t2)
register struct tm *t1, *t2;
{
	return((t2->tm_sec - t1->tm_sec) +
		60*(t2->tm_min - t1->tm_min) +
		3600L*(t2->tm_hour - t1->tm_hour));
}
